home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Communication / Weather / Source / Process.m < prev    next >
Text File  |  1993-11-12  |  7KB  |  275 lines

  1. /*
  2.  * Process -- manage i/o with simple subprocesses.
  3.  * Considerably tweaked relative of /NextDeveloper/Examples/Subprocess.
  4.  * M. J. Hawley
  5.  * Copyright (c) MIT Media Laboratory
  6.  * mike@media-lab.mit.edu
  7.  */
  8. #import "Process.h"
  9.  
  10. @interface Process(Private)
  11. - childDidExit;
  12. - fdHandler:(int)theFd;
  13. @end
  14.  
  15. static void
  16. showError (const char *s, id delegate){ // ensure errors never get lost
  17.     if (delegate && [delegate respondsTo:@selector(processError:)])
  18.     [delegate perform:@selector(processError:) with:(void *)s];
  19.     else if (NXApp)    // no delegate, but we're running w/in an App
  20.     NXRunAlertPanel(0, s, 0, 0, 0);
  21.     else
  22.     perror(s);
  23. }
  24.  
  25. static void
  26. fdHandler (int fd, id self) { // DPS handler for output from process
  27.     [self fdHandler:fd];
  28. }
  29.  
  30. static void
  31. getptys(int *master, int *slave){ // attempt to setup the ptys
  32.     #define    PTY_TEMPLATE "/dev/pty??"
  33.     #define    PTY_LENGTH 11
  34.     char device[PTY_LENGTH];
  35.     char *block, *num;
  36.     char *blockLoc; // specifies the location of block for the device string
  37.     char *numLoc; // specifies the pty name with the digit ptyxD
  38.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  39.     
  40.     struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  41.     struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  42.     struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  43.     int    lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  44.     int    setd = NTTYDISC;
  45.     
  46.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  47.     blockLoc = &device[ strlen("/dev/pty") ];
  48.     numLoc = &device[ strlen("/dev/pty?") ];
  49.     msLoc = &device[ strlen("/dev/") ];
  50.     for (block = "pqrs"; *block; block++){
  51.     *blockLoc = *block;
  52.     for (num = "0123456789abcdef"; *num; num++) {
  53.         *numLoc = *num;
  54.         *master = open(device, O_RDWR);
  55.         if (*master >= 0) {
  56.         *msLoc = 't';
  57.         *slave = open(device, O_RDWR);
  58.         if (*slave >= 0) {
  59.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  60.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  61.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  62.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  63.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  64.             return;
  65.         }
  66.         }
  67.     } /* hunting through a bank of ptys */
  68.     } /* hunting through blocks of ptys in all the right places */
  69.     *master = -1;
  70.     *slave = -1;
  71. }
  72.  
  73. static int
  74. iwait(fd, timeout)
  75.         long fd;
  76.         unsigned long timeout;  /* in seconds */
  77. /*
  78.  * Wait until 'fd' is ready for reading, or 'timeout'.
  79.  * Return '>=0' when 'fd' is readable, '0' if timeout, '-1' on error.
  80.  * Example: 'iwait(f,0)' polls a file descriptor
  81.  * without blocking and returns true if it's readable;
  82.  * e.g., 'iwait(0,0)' is true when standard input is contains something.
  83.  */
  84. {
  85.         struct timeval t;
  86.         int readfd = 1<<fd;
  87.  
  88.         t.tv_sec = timeout, t.tv_usec = 0;
  89.         return (int)select(sizeof(int)*8,  (fd_set *)&readfd, 
  90.                        (fd_set *)0, (fd_set *)0, &t);
  91. }
  92.  
  93. @implementation Process(Private)
  94.  
  95. - childDidExit { // cleanup after a child process exits
  96.     if (childPid) {
  97.     if (from) DPSRemoveFD(from);
  98.     close(from);
  99.     if (fpTo) fclose(fpTo);
  100.     if (fpFrom) fclose(fpFrom);
  101.     fpTo = fpFrom = (FILE *)0;
  102.     childPid=0;    // specify that child is dead
  103.     if (delegate && [delegate respondsTo:@selector(processDone)])
  104.         [delegate perform:@selector(processDone)];
  105.     }
  106.     return self;
  107. }
  108.  
  109. - fdHandler:(int)fd { // DPS handler for output from process
  110.     if (iwait(fd,1)<=0) return self;
  111.     if (((bufferCount=read(fd,buffer,BUFSIZE-1))<0)||(!bufferCount))
  112.     return [self childDidExit];
  113.     buffer[bufferCount] = '\0';
  114.     if (delegate && [delegate respondsTo:action])
  115.     [delegate perform:action with:(void *)&buffer];
  116.     return self;
  117. }
  118.  
  119. @end
  120.  
  121.  
  122. @implementation Process
  123.  
  124. + new:(char *)process delegate:del {
  125.     self = [Process alloc];
  126.     [self init:process delegate:del andPty:YES andStderr:YES];
  127.     return self;
  128. }
  129.  
  130. + new:(char *)process delegate:del andPty:(BOOL)wantsPty andStderr:(BOOL)wantsStderr {
  131.     self = [Process alloc];
  132.     [self init:process delegate:del andPty:wantsPty andStderr:wantsStderr];
  133.     return self;
  134. }
  135.  
  136. - init:(char *)process delegate:del {
  137.     return [self init:process delegate:del andPty:NO andStderr:NO];
  138. }
  139.  
  140. - init:(char *)process delegate:del andPty:(BOOL)pty andStderr:(BOOL)err {
  141.     // initializes an instance of process and corresponding UNIX process
  142.     int pipeTo[2];
  143.     int pipeFrom[2];
  144.     int    tty, numFds, fd;    // for temporary use
  145.     int processGroup;
  146.     int pidChild;        // needed because childPid does not exist
  147.                 // until process is instantiated
  148.  
  149.     [self setAction:@selector(processOutput:)];
  150.     if (pty){
  151.         tty = open("/dev/tty", O_RDWR);
  152.     getptys(&masterPty,&slavePty);
  153.     if (masterPty <= 0 || slavePty <= 0) {
  154.         showError("Error grabbing ptys for subprocess.", del);
  155.         return self;
  156.     }
  157.     // remove the controlling tty if launched from a shell,
  158.     // but not Workspace;
  159.     // so that we have job control over the parent application in shell
  160.     // and so that subprocesses can be restarted in Workspace
  161.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0)) {
  162.         ioctl(tty, TIOCNOTTY, 0);
  163.         close(tty);
  164.     }
  165.     } else
  166.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0){
  167.     showError("Error starting UNIX pipes to process.", del);
  168.     return self;
  169.     }
  170.     
  171.     switch (pidChild = vfork()){
  172.     case -1:    // error
  173.     showError("Error starting UNIX vfork of process.", del);
  174.     return self;
  175.  
  176.     case 0:    // child
  177.     if (pty) {
  178.         dup2(slavePty, 0);
  179.         dup2(slavePty, 1);
  180.         if (err) dup2(slavePty, 2);
  181.     } else {
  182.         dup2(pipeTo[0], 0);
  183.         dup2(pipeFrom[1], 1);
  184.         if (err) dup2(pipeFrom[1], 2);
  185.     }
  186.     
  187.     numFds = getdtablesize();
  188.     for (fd=3; fd<numFds; fd++)
  189.         close(fd);
  190.  
  191.     processGroup = getpid();
  192.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  193.     setpgrp (0, processGroup);
  194.     
  195.     // execl(process, 0);
  196.     execl("/bin/sh", "sh", "-c", process, 0);
  197.     perror("vfork (child)"); // should never gets here tho
  198.     exit(1);
  199.  
  200.     default:    // parent
  201.     [self setDelegate:del];
  202.     childPid = pidChild;
  203.     if (pty){
  204.         close(slavePty);
  205.         fpTo = fdopen(masterPty, "w");
  206.         from = masterPty;
  207.         fpFrom = fdopen(masterPty, "r");
  208.     } else {
  209.         close(pipeTo[0]);
  210.         close(pipeFrom[1]);
  211.     
  212.         fpTo = fdopen(pipeTo[1], "w");
  213.         from = pipeFrom[0];
  214.         fpFrom = fdopen(pipeFrom[0], "r");
  215.         }
  216.     setbuf(fpTo, NULL);
  217.     setbuf(fpFrom, NULL);
  218.     DPSAddFD(from,(DPSFDProc)fdHandler,(id)self,NX_MODALRESPTHRESHOLD+1);
  219.     // printf("added %d, %d, %d [%d %d]\n",from, self, delegate, pipeTo[1], pipeFrom[0]);
  220.     return self;
  221.     }
  222. }
  223.  
  224. - puts:(char *)s {
  225.     fputs(s, fpTo);
  226.     return self;
  227. }
  228.  
  229.  
  230. static void
  231. _fgets(s,n,f) char *s; int n; FILE *f; {
  232.     int fd = fileno(f);
  233.     while (iwait(fd,3)>0){
  234.         if (read(fd,s,1) != 1){
  235.             *s = '\0';
  236.             return;
  237.         }
  238.         if (*s=='\n' || --n <= 0){
  239.             *++s = '\0';
  240.             return ;
  241.         }
  242.         if (*s != '\r') ++s;
  243.     }
  244.     *++s = '\0';
  245. }
  246.  
  247. - gets:(char *)s :(int)n {
  248.     *s = '\0';
  249.     _fgets(s,n,fpFrom);
  250.     return self;
  251. }
  252.  
  253. - terminate:sender{
  254.     if (childPid){
  255.     kill(childPid+1, SIGTERM);
  256.     [self childDidExit];
  257.     }
  258.     return self;
  259. }
  260.  
  261. - setDelegate:anObject {
  262.     delegate = anObject;
  263.     return self;
  264. }
  265.  
  266. - delegate {
  267.     return delegate;
  268. }
  269.  
  270. - setAction:(SEL)theAction {
  271.     action = theAction;
  272.     return self;
  273. }
  274. @end
  275.